#!/usr/bin/perl

package Swignition::Export::BlogEntry;
use Swignition::Export;
use Swignition::Export::Contact;
use Swignition::MagicString;
use HTML::Entities qw(encode_entities_numeric);
use MIME::Base64;

sub is
{
	my $subject = shift;
	return unless ($subject);
	return unless (ref $subject eq 'Swignition::RDFModel::Subject');	
	return
		unless (defined $subject->property('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'));
	foreach my $Type ( @{$subject
		->property('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
		->valObjects} )
	{
		my $type = $Type->toString;
		return 1
			if (($type eq 'http://www.w3.org/2002/12/cal/ical#Vjournal')
			||  ($type eq 'http://purl.invalid/stuff/atom/owl#Entry')
			||  ($type eq 'http://bblfish.net/work/atom-owl/2006-06-06/#Entry')
			||  ($type eq 'http://purl.org/rss/1.0/item'));
	}
	return;
}

sub outputs
{
	return [
		{
			code  => 'ICS',
			name  => 'iCalendar',
			type  => 'text/directory',
			multi => 1
		},
		{
			code  => 'Atom',
			name  => 'Atom Feed',
			type  => 'application/atom+xml'
		}
	];
}


sub _outAtom
{
	my $triples = $_[0];
	my $e       = $_[1];
	my $rv      = '';
	
	my $E = findSubject($triples, $e);
	
	# Todo: GeoRSS!
	
	$rv .= "\t<entry>\n";

	my $link = firstOf($E,
		'http://www.iana.org/assignments/relation/self',
		'http://purl.org/rss/1.0/link');
	$link = $e unless length $link;
	my $id = firstOf($E,
		'http://purl.invalid/stuff/atom/owl#id',
		'http://bblfish.net/work/atom-owl/2006-06-06/#id',
		'http://www.w3.org/2002/12/cal/ical#uid',
		'http://purl.org/dc/terms/identifier',
		'http://purl.org/dc/elements/1.1/identifier');
	$id = $link unless length $id;
	$rv .= "\t\t<id>".encode_entities_numeric($id)."</id>\n" if ($id);
	$rv .= "\t\t<link rel=\"self\" href=\"".encode_entities_numeric($link)."\" />\n"
		if ($link && ($link !~ m|^bnode:///|));

	my @inReplyTos = allOf($E, 'http://buzzword.org.uk/rdf/atomix#in-reply-to');
	foreach my $inReplyTo (@inReplyTos)
	{
		my $parent = findSubject($triples, $inReplyTo);
		if ($parent)
		{
			my $id = firstOf($parent,
				'http://www.iana.org/assignments/relation/self',
				'http://purl.org/rss/1.0/link',
				'http://www.w3.org/2002/12/cal/ical#uid',
				'http://purl.invalid/stuff/atom/owl#id',
				'http://bblfish.net/work/atom-owl/2006-06-06/#id',
				'http://www.w3.org/2002/12/cal/ical#uid',
				'http://purl.org/dc/terms/identifier',
				'http://purl.org/dc/elements/1.1/identifier');
			$id = $inReplyTo if (!$id);
			if ($id)
			{
				if ($id =~ m|^bnode:///|)
				{
					$rv .= "\t\t<thr:in-reply-to ref=\"".encode_entities_numeric($id)."\" />\n";
				}
				else
				{
					$rv .= "\t\t<thr:in-reply-to href=\"".encode_entities_numeric($id)."\"\n\t\t                 ref=\"".encode_entities_numeric($id)."\" />\n";
					$rv .= "\t\t<link rel=\"related\" href=\"".encode_entities_numeric($id)."\" />\n";
				}
			}
		}
	}

	my @replies = allOf($E,
		'http://www.iana.org/assignments/relation/replies',
		'http://buzzword.org.uk/rdf/atomix#replies');
	my $appropriate_link = 0;
	foreach my $r (@replies)
	{
		next if ($r =~ m|^bnode:///|);
		$rv .= "\t\t<link rel=\"replies\" href=\"".encode_entities_numeric($r)."\" />\n";
		$appropriate_link++;
	}
	if (@replies && !$appropriate_link)
	{
		$rv .= "\t\t<link rel=\"replies\" href=\"".encode_entities_numeric($r)."\" />\n"
			if ($r !~ m|^bnode:///|);
	}

	my @contents = allOfObj($E,
		'http://purl.invalid/stuff/atom/owl#title',
		'http://bblfish.net/work/atom-owl/2006-06-06/#title',
		'http://purl.org/rss/1.0/title',
		'http://purl.org/dc/terms/title',
		'http://purl.org/dc/elements/1.1/title',
		'http://www.w3.org/2000/01/rdf-schema#label',
		'http://www.w3.org/2002/12/cal/ical#summary');
	my $cnt;
	my $cnt_type;
	if (@contents)
	{
		foreach my $c (@contents)
		{
			if ($c->type)
			{
				my $C = findSubject($triples, $c->toString);
				my $content = Swignition::Export::Feed::AtomOwlContent($C);
				if ($content)
				{
					$cnt_type = $content->[0];
					$cnt      = $content->[1];
					last;
				}
			}
			else
			{
				$cnt_type = 'text/plain';
				$cnt      = $c->toString;
				last;
			}
		}
	}
	if ($cnt)
	{
		$cnt_type = 'html'
			if ($cnt_type =~ m#^(text/html|application/xhtml+xml|application/vnd.wap.xhtml+xml)$#i);
		$cnt_type = 'text'
			if ($cnt_type =~ m#^(text/plain)$#);
	
		if (isms($cnt))
		{
			$rv .= sprintf("\t\t<title xml:lang=\"%s\" type=\"%s\">%s</title>\n",
				encode_entities_numeric($cnt->{lang}),
				encode_entities_numeric($cnt_type),
				encode_entities_numeric("$cnt"));
		}
		else
		{
			$rv .= sprintf("\t\t<title type=\"%s\">%s</title>\n",
				encode_entities_numeric($cnt_type),
				encode_entities_numeric($cnt));
		}
	}
	else
	{
		$rv .= "\t\t<title xml:lang=\"en\">Untitled</title>\n";
	}

	my @contents = allOfObj($E,
		'http://purl.invalid/stuff/atom/owl#summary',
		'http://bblfish.net/work/atom-owl/2006-06-06/#summary',
		'http://purl.org/dc/terms/abstract',
		'http://www.w3.org/2002/12/cal/ical#comment');
	my $cnt;
	my $cnt_type;
	if (@contents)
	{
		foreach my $c (@contents)
		{
			if ($c->type)
			{
				my $C = findSubject($triples, $c->toString);
				my $content = Swignition::Export::Feed::AtomOwlContent($C);
				if ($content)
				{
					$cnt_type = $content->[0];
					$cnt      = $content->[1];
					last;
				}
			}
			else
			{
				$cnt_type = 'text/plain';
				$cnt      = $c->toString;
				last;
			}
		}
	}
	if ($cnt)
	{
		$cnt_type = 'html'
			if ($cnt_type =~ m#^(text/html|application/xhtml+xml|application/vnd.wap.xhtml+xml)$#i);
		$cnt_type = 'text'
			if ($cnt_type =~ m#^(text/plain)$#);
	
		if (isms($cnt))
		{
			$rv .= sprintf("\t\t<summary xml:lang=\"%s\" type=\"%s\">%s</summary>\n",
				encode_entities_numeric($cnt->{lang}),
				encode_entities_numeric($cnt_type),
				encode_entities_numeric("$cnt"));
		}
		else
		{
			$rv .= sprintf("\t\t<summary type=\"%s\">%s</summary>\n",
				encode_entities_numeric($cnt_type),
				encode_entities_numeric($cnt));
		}
	}
	
	my @contents = allOfObj($E,
		'http://purl.invalid/stuff/atom/owl#content',
		'http://bblfish.net/work/atom-owl/2006-06-06/#content',
		'http://purl.org/rss/1.0/modules/content/encoded',
		'http://web.resource.org/rss/1.0/modules/content/encoded',
		'http://purl.org/rss/1.0/description',
		'http://purl.org/dc/terms/description',
		'http://purl.org/dc/elements/1.1/description',
		'http://www.w3.org/2002/12/cal/ical#description');
	my $cnt;
	my $cnt_type;
	if (@contents)
	{
		foreach my $c (@contents)
		{
			if ($c->type)
			{
				my $C = findSubject($triples, $c->toString);
				my $content = Swignition::Export::Feed::AtomOwlContent($C);
				if ($content)
				{
					$cnt_type = $content->[0];
					$cnt      = $content->[1];
					last;
				}
			}
			else
			{
				$cnt_type = 'text/plain';
				$cnt_type = 'text/html'
					if ($c->property->uri eq 'http://purl.org/rss/1.0/modules/content/encoded'
					||  $c->property->uri eq 'http://web.resource.org/rss/1.0/modules/content/encoded');
				$cnt      = $c->toString;
				last;
			}
		}
	}
	if ($cnt)
	{
		$cnt_type = 'html'
			if ($cnt_type =~ m#^(text/html|application/xhtml+xml|application/vnd.wap.xhtml+xml)$#i);
		$cnt_type = 'text'
			if ($cnt_type =~ m#^(text/plain)$#);
	
		if (isms($cnt))
		{
			$rv .= sprintf("\t\t<content xml:lang=\"%s\" type=\"%s\">%s</content>\n",
				encode_entities_numeric($cnt->{lang}),
				encode_entities_numeric($cnt_type),
				encode_entities_numeric("$cnt"));
		}
		else
		{
			$rv .= sprintf("\t\t<content type=\"%s\">%s</content>\n",
				encode_entities_numeric($cnt_type),
				encode_entities_numeric($cnt));
		}
	}
	elsif ($link && ($link !~ m|^bnode:///|))
	{
		$rv .= "\t\t<link rel=\"alternate\" href=\"".encode_entities_numeric($link)."\" />\n";
	}
	
	my @encs = allOfObj($E,
		'http://www.iana.org/assignments/relation/enclosure',
		'urn:ietf:rfc:4287#enclosure',
		'urn:ietf:rfc:2445#attach',
		'http://www.w3.org/2002/12/cal/ical#attach');
	foreach my $enc (@encs)
	{
		my $E = findSubject($triples, $enc->toString);
		if ($E)
		{
			my $euri = $E->firstVal('http://www.iana.org/assignments/relation/enclosure');
			if ($euri)
			{
				$rv .= "\t\t<link rel=\"enclosure\" href=\"".encode_entities_numeric($euri)."\" />\n";
			}
		}
		elsif ($enc->type)
		{
			$rv .= "\t\t<link rel=\"enclosure\" href=\"".encode_entities_numeric($enc->toString)."\" />\n";
		}
	}

	my @authors = allOfObj($E,
		'http://purl.invalid/stuff/atom/owl#author',
		'http://bblfish.net/work/atom-owl/2006-06-06/#author',
		'http://purl.org/dc/terms/creator',
		'http://purl.org/dc/elements/1.1/creator',
		'http://xmlns.com/foaf/0.1/maker',
		'http://www.w3.org/2002/12/cal/ical#organizer');
	foreach my $author (@authors)
	{
		my $authorSubject = findSubject($triples, $author->toString);
		if ($authorSubject && Swignition::Export::Contact::is($authorSubject))
		{
			$rv .= Swignition::Export::Contact::_AtomPerson(
				$triples,
				$authorSubject->uri,
				"\t\t<author>");
		}
		elsif ($author->type)
		{
			$rv .= "\t\t<author><url>".$author->toString."</url></author>\n";
		}
		else
		{
			$rv .= "\t\t<author><name>".$author->toString."</name></author>\n";
		}
	}

	my @contributors = allOfObj($E,
		'http://purl.invalid/stuff/atom/owl#contributor',
		'http://bblfish.net/work/atom-owl/2006-06-06/#contributor',
		'urn:ietf:rfc:4287#contributor',
		'http://purl.org/dc/terms/contributor',
		'http://purl.org/dc/elements/1.1/contributor');
	foreach my $contributor (@contributors)
	{
		my $contributorSubject = findSubject($triples, $contributor->toString);
		if ($contributorSubject && Swignition::Export::Contact::is($contributorSubject))
		{
			$rv .= Swignition::Export::Contact::_AtomPerson(
				$triples,
				$contributorSubject->uri,
				"\t\t<contributor>");
		}
		elsif ($contributor->type)
		{
			$rv .= "\t\t<contributor><url>".$contributor->toString."</url></contributor>\n";
		}
		else
		{
			$rv .= "\t\t<contributor><name>".$contributor->toString."</name></contributor>\n";
		}
	}

	my @cats = allOfObj($E,
		'http://purl.invalid/stuff/atom/owl#category',
		'http://bblfish.net/work/atom-owl/2006-06-06/#category',
		'urn:ietf:rfc:4287#category',
		'urn:ietf:rfc:2445#categories',
		'http://www.w3.org/2002/12/cal/ical#categories',
		'http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedWithTag');
	foreach my $cat (@cats)
	{
		my ($tag, $scheme, $label);
		my $catSubject = findSubject($triples, $cat->toString);
		
		if ($catSubject)
		{
			$tag    = firstOf($catSubject,
				'http://purl.invalid/stuff/atom/owl#name',
				'http://bblfish.net/work/atom-owl/2006-06-06/#name',
				'http://www.holygoat.co.uk/owl/redwood/0.1/tags/name',
				'http://www.w3.org/1999/02/22-rdf-syntax-ns#value',
				'http://purl.org/dc/terms/title',
				'http://purl.org/dc/elements/1.1/title');
			$scheme = firstOf($catSubject,
				'http://purl.invalid/stuff/atom/owl#scheme',
				'http://bblfish.net/work/atom-owl/2006-06-06/#scheme');
			$label  = firstOf($catSubject,
				'http://purl.invalid/stuff/atom/owl#label',
				'http://bblfish.net/work/atom-owl/2006-06-06/#label',
				'http://www.w3.org/2000/01/rdf-schema#label',
				'http://www.holygoat.co.uk/owl/redwood/0.1/tags/name',
				'http://www.w3.org/1999/02/22-rdf-syntax-ns#value',
				'http://purl.org/dc/terms/title',
				'http://purl.org/dc/elements/1.1/title');
		}
		$tag   = $cat->toString if (!$tag);
		$label = $cat->toString if (!$label);
		
		$rv .= "\t\t<category term=\"".encode_entities_numeric($tag)."\" ";
		$rv .= "scheme=\"".encode_entities_numeric($scheme)."\" "
			if (length $scheme);
		$rv .= "label=\"".encode_entities_numeric($label)."\" "
			if (length $label);
		$rv .= "/>\n";
	}

	my $rights = firstOfLiteral($E,
		['http://purl.invalid/stuff/atom/owl#rights',
		'http://bblfish.net/work/atom-owl/2006-06-06/#rights',
		'urn:ietf:rfc:4287#rights',
		'http://purl.org/dc/terms/rights',
		'http://purl.org/dc/elements/1.1/rights'],
		['http://purl.invalid/stuff/atom/owl#xhtml',
		'http://bblfish.net/work/atom-owl/2006-06-06/#xhtml',
		'http://purl.invalid/stuff/atom/owl#html',
		'http://bblfish.net/work/atom-owl/2006-06-06/#html',
		'http://purl.invalid/stuff/atom/owl#text',
		'http://bblfish.net/work/atom-owl/2006-06-06/#text',
		'urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	$rv .= "\t\t<rights>".encode_entities_numeric($rights)."</rights>\n"
		if ($rights);

	my $published = firstOf($E,
		'http://purl.invalid/stuff/atom/owl#published',
		'http://bblfish.net/work/atom-owl/2006-06-06/#published',
		'http://purl.org/dc/terms/available',
		'http://purl.org/dc/terms/created',
		'http://www.w3.org/2002/12/cal/ical#created',
		'http://www.w3.org/2005/Atompublished',
		'http://www.w3.org/2005/Atom#published');
	$published = Swignition::Export::Feed::AtomDate($published);
	$rv .= "\t\t<published>".encode_entities_numeric($published)."</published>\n"
		if ($published);

	my $updated = firstOf($E,
		'http://purl.invalid/stuff/atom/owl#updated',
		'http://bblfish.net/work/atom-owl/2006-06-06/#updated',
		'http://purl.org/dc/terms/modified',
		'http://purl.org/dc/terms/date',
		'http://purl.org/dc/elements/1.1/date',
		'http://www.w3.org/2002/12/cal/ical#dtstamp',
		'http://www.w3.org/2002/12/cal/ical#lastModified',
		'http://www.w3.org/2005/Atomupdated',
		'http://www.w3.org/2005/Atom#updated',
		'http://purl.org/rss/1.0/modules/rss091#pubDate',
		'http://purl.org/rss/1.0/modules/rss091#lastBuildDate');
	if (!$updated)
	{
		$updated = $published;
	}
	if ($updated)
	{
		$updated = Swignition::Export::Feed::AtomDate($updated);
		$rv .= "\t\t<updated>".encode_entities_numeric($updated)."</updated>\n";
		$_[2] = $updated
			if ((''.$_[2]) lt "$updated");
	}
	
	$rv .= "\t</entry>\n";

	return $rv;
}


sub outICS
{
	return "BEGIN:VCALENDAR\r\n"
		. _outICS(@_)
		. "END:VCALENDAR\r\n";
}

sub multiICS
{
	my $triples = shift;
	my $rv = '';
	foreach my $s (keys %$triples)
	{
		$rv .= _outICS($triples, $s)
			if (defined $triples->{$s} && is($triples->{$s}));
	}

	return "BEGIN:VCALENDAR\r\n"
		. $rv
		. "END:VCALENDAR\r\n";
}

sub _outICS
{
	my $triples = shift;
	my $subject = shift;
	my $S = findSubject($triples, $subject);
	my @rv = (['BEGIN','VJOURNAL']);

	# UID
	my $uid = firstOf($S, 'urn:ietf:rfc:2445#uid',
		'http://www.w3.org/2002/12/cal/ical#uid',
		'http://purl.org/dc/terms/identifier',
		'http://purl.org/dc/elements/1.1/identifier',
		'http://www.iana.org/assignments/relation/self',
		'urn:ietf:rfc:4287#link',
		'http://purl.org/rss/1.0/link');
	push @rv, ['UID', $uid]
		if ($uid);
	
	# Related-To;RelType=Child
	my @kids = allOf($S,
		'urn:ietf:rfc:2445#child',
		'http://buzzword.org.uk/rdf/icalx#child');
	foreach my $kid (@kids)
	{
		my $kidSubject = findSubject($triples, $kid);
		if ($kidSubject)
		{
			my $kUid = firstOf($kidSubject, 'urn:ietf:rfc:2445#uid',
				'http://www.w3.org/2002/12/cal/ical#uid',
				'http://purl.org/dc/terms/identifier',
				'http://purl.org/dc/elements/1.1/identifier',
				'http://www.iana.org/assignments/relation/self',
				'urn:ietf:rfc:4287#link',
				'http://purl.org/rss/1.0/link');
			if ($kUid)
			{
				push @rv, ['RELATED-TO;RELTYPE=CHILD', $kUid]
			}
		}
	}

	# Related-To;RelType=Sibling
	my @sibs = allOf($S, 'urn:ietf:rfc:2445#sibling',
		'http://buzzword.org.uk/rdf/icalx#sibling',
		'http://www.w3.org/2002/12/cal/ical#relatedTo');
	foreach my $sib (@sibs)
	{
		my $sibSubject = findSubject($triples, $sib);
		if ($sibSubject)
		{
			my $sUid = firstOf($triples->{$sib}, 'urn:ietf:rfc:2445#uid',
				'http://www.w3.org/2002/12/cal/ical#uid',
				'http://purl.org/dc/terms/identifier',
				'http://purl.org/dc/elements/1.1/identifier',
				'http://www.iana.org/assignments/relation/self',
				'urn:ietf:rfc:4287#link',
				'http://purl.org/rss/1.0/link');
			if ($sUid)
			{
				push @rv, ['RELATED-TO;RELTYPE=SIBLING', $sUid]
			}
		}
	}

	# Related-To;RelType=Parent
	my $parent = firstOf($S, 'urn:ietf:rfc:2445#parent',
		'http://buzzword.org.uk/rdf/atomix#in-reply-to',
		'http://buzzword.org.uk/rdf/icalx#parent');
	if ($parent)
	{
		my $parentSubject = findSubject($triples, $parent);
		if ($parentSubject)
		{
			my $pUid = firstOf($parentSubject, 'urn:ietf:rfc:2445#uid',
				'http://www.w3.org/2002/12/cal/ical#uid',
				'http://purl.org/dc/terms/identifier',
				'http://purl.org/dc/elements/1.1/identifier',
				'http://www.iana.org/assignments/relation/self',
				'urn:ietf:rfc:4287#link',
				'http://purl.org/rss/1.0/link');
			if ($pUid)
			{
				push @rv, ['RELATED-TO;RELTYPE=PARENT', $pUid]
			}
		}
	}

	# Attach
	my @attachments = allOfObj($S,
		'http://www.iana.org/assignments/relation/enclosure',
		'urn:ietf:rfc:2445#attach',
		'http://www.w3.org/2002/12/cal/ical#attach',
		'http://purl.org/dc/terms/hasPart',
		'urn:ietf:rfc:4287#enclosure');
	foreach my $att (@attachments)
	{
		my $Att = findSubject($triples, $att->toString);
		if ($Att)
		{
			my $e = $Att->firstVal('http://www.iana.org/assignments/relation/enclosure');
			push @rv, ['ATTACH;VALUE=uri',$e] if ($e);
		}
		elsif ($att->type)
		{
			push @rv, ['ATTACH;VALUE=uri',$att->toString];
		}
		else
		{
			push @rv, ['ATTACH',$att->toString];
		}
	}
	
	# Categories
	my @cats = allOfObj($S,
		'http://purl.invalid/stuff/atom/owl#category',
		'http://bblfish.net/work/atom-owl/2006-06-06/#category',
		'urn:ietf:rfc:4287#category',
		'urn:ietf:rfc:2445#categories',
		'http://www.w3.org/2002/12/cal/ical#categories',
		'http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedWithTag');
	my $catstring = '';
	foreach my $cat (@cats)
	{
		my $catSubject = findSubject($triples, $cat->toString);
		if ($catSubject)
		{
			my $category = firstOf($catSubject,
				'http://purl.invalid/stuff/atom/owl#name',
				'http://bblfish.net/work/atom-owl/2006-06-06/#name',
				'http://www.holygoat.co.uk/owl/redwood/0.1/tags/name',
				'http://www.w3.org/1999/02/22-rdf-syntax-ns#value',
				'http://purl.org/dc/terms/title',
				'http://purl.org/dc/elements/1.1/title');
			$catstring .= ','.uc($category)
				if (length $category);
		}
		else
		{
			my $category = $cat->toString;
			$category =~ s/(^\s+|\s+$)//g;
			$category =~ s/\s+/ /g;
			$catstring .= ','.uc($category);
		}
	}
	$catstring =~ s/^\,//;
	push @rv, ['CATEGORIES',$catstring,1]
		if ($catstring);

	# Class
	my $class = firstOfLiteral($S,
		['urn:ietf:rfc:2445#class',
		'http://www.w3.org/2002/12/cal/ical#class'],
		['urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	push @rv, ['CLASS', uc($class)]
		if ($class);
		
	# Taxonomy
	my @species = allOfObj($S,
		'http://purl.org/NET/biol/auxiliary#sightingOf');
	foreach my $species (@species)
	{
		my $speciesSubject = findSubject($triples, $species->toString);
		if ($speciesSubject)
		{
			if (Swignition::Export::Taxonomy::is($speciesSubject))
			{
				my $val = Swignition::Export::Taxonomy::outText($triples, $speciesSubject->uri);
				push @rv, ['X-SIGHTING-OF', $val];
			}
		}
		elsif (!$species->type)
		{
			push @rv, ['X-SIGHTING-OF', $species->toString, 1];
		}
	}

	my @contents = allOfObj($S,
		'http://purl.invalid/stuff/atom/owl#summary',
		'http://bblfish.net/work/atom-owl/2006-06-06/#summary',
		'http://purl.org/dc/terms/abstract',
		'http://www.w3.org/2002/12/cal/ical#comment');
	my $cnt;
	my $cnt_type;
	if (@contents)
	{
		foreach my $c (@contents)
		{
			if ($c->type)
			{
				my $C = findSubject($triples, $c->toString);
				my $content = Swignition::Export::Feed::AtomOwlContent($C);
				if ($content)
				{
					$cnt_type = $content->[0];
					$cnt      = $content->[1];
					last;
				}
			}
			else
			{
				$cnt_type = 'text/plain';
				$cnt      = $c->toString;
				last;
			}
		}
	}
	if ($cnt)
	{
		push @rv, ['COMMENT', $cnt];
	}

	# Geo
	# Todo: GeoRSS!
	my @geos = allOfObj($S, 'urn:ietf:rfc:2445#geo',
		'http://www.w3.org/2002/12/cal/ical#geo');
	foreach my $geo (@geos)
	{
		my $geoSubject = findSubject($triples, $geo->toString);
		if ($geoSubject)
		{
			if (Swignition::Export::Point::is($geoSubject))
			{
				my ($key, $val) = Swignition::Export::Point::outVCFLine($triples, $geoSubject->uri);
				push @rv, [$key, $val, 1];
			}
		}
	}

	# description
	my @contents = allOfObj($S,
		'http://purl.invalid/stuff/atom/owl#content',
		'http://bblfish.net/work/atom-owl/2006-06-06/#content',
		'http://purl.org/rss/1.0/description',
		'http://purl.org/dc/terms/description',
		'http://purl.org/dc/elements/1.1/description',
		'http://www.w3.org/2002/12/cal/ical#description');
	my $cnt;
	my $cnt_type;
	if (@contents)
	{
		foreach my $c (@contents)
		{
			if ($c->type)
			{
				my $C = findSubject($triples, $c->toString);
				my $content = Swignition::Export::Feed::AtomOwlContent($C);
				if ($content)
				{
					$cnt_type = $content->[0];
					$cnt      = $content->[1];
					last;
				}
			}
			else
			{
				$cnt_type = 'text/plain';
				$cnt      = $c->toString;
				last;
			}
		}
	}
	if ($cnt)
	{
		push @rv, ['DESCRIPTION', $cnt];
	}
	
	# Status
	my $status = firstOfLiteral($S,
		['urn:ietf:rfc:2445#status',
		'http://www.w3.org/2002/12/cal/ical#status'],
		['urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	push @rv, ['STATUS', uc($status)]
		if ($status);	

	my @contents = allOfObj($S,
		'http://purl.invalid/stuff/atom/owl#title',
		'http://bblfish.net/work/atom-owl/2006-06-06/#title',
		'http://purl.org/rss/1.0/title',
		'http://purl.org/dc/terms/title',
		'http://purl.org/dc/elements/1.1/title',
		'http://www.w3.org/2000/01/rdf-schema#label',
		'http://www.w3.org/2002/12/cal/ical#summary');
	my $cnt;
	my $cnt_type;
	if (@contents)
	{
		foreach my $c (@contents)
		{
			if ($c->type)
			{
				my $C = findSubject($triples, $c->toString);
				my $content = Swignition::Export::Feed::AtomOwlContent($C);
				if ($content)
				{
					$cnt_type = $content->[0];
					$cnt      = $content->[1];
					last;
				}
			}
			else
			{
				$cnt_type = 'text/plain';
				$cnt      = $c->toString;
				last;
			}
		}
	}
	if ($cnt)
	{
		push @rv, ['SUMMARY', $cnt];
	}

	# URL
	my $url = firstOf($S, 
		'http://www.w3.org/2002/12/cal/ical#url',
		'http://www.iana.org/assignments/relation/self',
		'http://purl.org/rss/1.0/link');
	push @rv, ['URL', $url]
		if ($url);
		
	# Dates and stuff
	my $dt = firstOfLiteral($S,
		['http://purl.invalid/stuff/atom/owl#published',
		'http://bblfish.net/work/atom-owl/2006-06-06/#published',
		'urn:ietf:rfc:2445#created',
		'http://www.w3.org/2002/12/cal/ical#created',
		'urn:ietf:rfc:4287#published',
		'http://purl.org/dc/terms/available',
		'http://purl.org/dc/terms/created'],
		['urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	push @rv, ['CREATED', Swignition::Export::CalComponent::_dt($dt)] if ($dt);
	$dt = firstOfLiteral($S,
		['urn:ietf:rfc:2445#dtstamp',
		'http://www.w3.org/2002/12/cal/ical#dtstamp',
		'http://purl.invalid/stuff/atom/owl#updated',
		'http://bblfish.net/work/atom-owl/2006-06-06/#updated',
		'urn:ietf:rfc:4287#updated',
		'http://purl.org/dc/terms/modified',
		'http://purl.org/dc/terms/date',
		'http://purl.org/dc/elements/1.1/date'],
		['urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	push @rv, ['DTSTAMP', Swignition::Export::CalComponent::_dt($dt)] if ($dt);
	$dt = firstOfLiteral($S,
		['urn:ietf:rfc:2445#last-modified',
		'http://www.w3.org/2002/12/cal/ical#lastModified',
		'http://purl.invalid/stuff/atom/owl#updated',
		'http://bblfish.net/work/atom-owl/2006-06-06/#updated',
		'urn:ietf:rfc:4287#updated',
		'http://purl.org/dc/terms/modified',
		'http://purl.org/dc/terms/date',
		'http://purl.org/dc/elements/1.1/date'],
		['urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	push @rv, ['LAST-MODIFIED', Swignition::Export::CalComponent::_dt($dt)] if ($dt);

	# RDATE, RRULE, EXDATE, EXRULE - o'reilly?!
	foreach my $p (@{['rdate', 'rrule', 'exdate', 'exrule']})
	{
		my @R = allOfLiteral($S,
			['urn:ietf:rfc:2445#'.$p,
			'http://www.w3.org/2002/12/cal/ical#'.$p],
			['urn:ietf:rfc:4287#value',
			'urn:ietf:rfc:2445#value',
			'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
		if ($p =~ /date$/)
		{
			foreach my $r (@R)
				{ push @rv, [uc($p), Swignition::Export::CalComponent::_dt($r), 1]; }
		}
		else
		{
			foreach my $r (@R)
				{ push @rv, [uc($p), $r, 1]; }
		}
	}

	# Recurrence-ID
	my $rid = firstOf($S, 'urn:ietf:rfc:2445#recurrence-id',
		'http://www.w3.org/2002/12/cal/ical#recurrenceId');
	push @rv, ['RECURRENCE-ID', $rid]
		if ($rid);

	# Sequence
	my $seq = firstOfLiteral($S,
		['urn:ietf:rfc:2445#sequence',
		'http://www.w3.org/2002/12/cal/ical#sequence'],
		['urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	push @rv, ['SEQUENCE', $seq] if ($seq);
	
	# Description
	my $desc = firstOfLiteral($S,
		['urn:ietf:rfc:2445#description',
		'http://www.w3.org/2002/12/cal/ical#description',
		'http://purl.org/dc/terms/description',
		'http://purl.org/dc/elements/1.1/description',
		'http://purl.org/dc/terms/abstract'],
		['urn:ietf:rfc:4287#value',
		'urn:ietf:rfc:2445#value',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#value']);
	push @rv, ['DESCRIPTION', $desc]
		if ($desc);
	
	# Attendees, Contact, Organizer
	push @rv, Swignition::Export::CalComponent::_people($triples, $S, 'aco');

	# Done.
	push @rv, ['END','VJOURNAL'];
	
	# Convert to text/directory syntax.
	my $rv = '';
	foreach my $line (@rv)
	{
		my ($k, $v, $soft) = @{$line};
		
		if ($k =~ /VALUE.uri$/i && $v =~ /^data\:/)
		{
			my $uri = URI->new($v);
			$v = encode_base64($uri->data, '');
			$k =~ s/VALUE.uri$/ENCODING\=b/i;
			$k .= ';TYPE='.$uri->media_type
				if ($uri->media_type);
		}
		
		$v =~ s/\\/\\\\/g;
		$v =~ s/\r?\n/\\n/g;
		unless ($soft)
		{
			$v =~ s/\;/\\\;/g;
			$v =~ s/\,/\\\,/g;
		}
		
		$rv .= "$k:$v\r\n";
	}
	return $rv;
}

1;
